Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Content-Security-Policy" to speculation rules explainer #209

Closed
wants to merge 1 commit into from

Conversation

toyoshim
Copy link
Contributor

@toyoshim toyoshim commented Nov 8, 2022

The new section explains how the prerender and prefetch follow the Content-Security-Policy. They are not restricted by script-src, but done by prefetch-src. This makes speculationrules safer and flexible.

The new section explains how the prerender and prefetch follow the
Content-Security-Policy. They are not restricted by `script-src`, but
done by `prefetch-src`. This makes speculationrules safer and flexible.
@toyoshim
Copy link
Contributor Author

toyoshim commented Nov 8, 2022

@jeremyroman can you take a look?

@mikewest
Copy link
Member

mikewest commented Nov 8, 2022

It's not clear to me that this is the right approach. The discussion @arturjanc started in w3c/webappsec-csp#472 landed on using the same script-src directive for this <script> tag as we use for others. That seems straightforward: why complicate the model?

@jeremyroman
Copy link
Collaborator

Yeah, I think we landed at script-src in that discussion, with prefetch-src referring to the traffic for the prefetches that may induce. A restrictive script-src can be worked around in the usual ways (nonce, hash, fetch from trusted origin), and if this is a problem in practice we can always add a more specific speculation-rules-src.

It's a little tricky because it's more active than most subresources would be, even though it's not as powerful as JavaScript.

@toyoshim
Copy link
Contributor Author

toyoshim commented Nov 8, 2022

The problem of using script-src is it doesn't permit to generate the speculation rules dynamically, or need to permit script-src unsafe-inline. But apparently, script-src unsafe-inline is not what we really want here. It's too relaxed and risky choice. On the other hand, we have strong requirements to prerender dynamically predicted target pages.

@jeremyroman
Copy link
Collaborator

I did mention that case in w3c/webappsec-csp#472 (comment). I don't know whether 'strict-dynamic' works here (it would be interesting to check), but besides that there are at least some workarounds, namely using nonces.

I could certainly see a separate directive (which should probably default to script-src rather than default-src), but prefetch-src doesn't seem quite appropriate to me.

@domenic
Copy link
Collaborator

domenic commented Nov 9, 2022

That seems straightforward: why complicate the model?

The main motivation is that we are only using <script> here as a way of embedding JSON. If there were a <json> element, we would use that. This is not actually scripting; it's more like a complicated version of <link rel="prefetch"> encoded into JSON and stuffed into the document as text. That's why we were suggesting prefetch-src be used, for controlling any fetches initiated by the speculation rules.

Note that this case is different than type="importmap" because type="importmap" can directly affect the execution of other script on the page, by changing what it imports. So that's arguably as powerful as a script (or a service worker). But type="speclationrules" is not in the same league of power.

@toyoshim
Copy link
Contributor Author

toyoshim commented Nov 9, 2022

@jeremyroman thank you for sharing your comment that discussed the same problem before.

In the comment, I agreed that there are two aspects to restrict prerendering; 1) speculationrules injection sources, and 2) prerendering targets. prefetch-src will be a solution for 2), and there are still some rooms to discuss for 1).

Having a dedicated rule like speculation-rules-src sounds a nice alternative to me. As @domenic commented, <script> is just a possible and existing implementation of speculationrules, and we may have multiple ways to provide it. In such case, it would be nicer to apply the dedicated common directive for all approaches rather than applying script-src only for the <script> case, or inventing separate restriction for each.

I'm quite new to strict-dynamic and need some researches to understand if it solves my problems. Let me check.

@mikewest
Copy link
Member

mikewest commented Nov 9, 2022

If there were a element, we would use that.

There's a <datalist> element which can contain phrasing content and which isn't rendered by default. Would it be reasonable to give it extra functionality (perhaps even growing JSON-related attributes if necessary, similar to Response) rather than extending <script>?

I don't have a strong objection to the model you're proposing here. I think I agree with you that controling the set of things that are prefetched isn't terribly dangerous, with the only risk being exfiltration that we should be able to handle via other rules. That said, I do prefer to keep developer's mental models around script-src and <script> simple. The one controls the other's behavior, no thinking about edge cases necessary. This is generally the model that CSP and the Sanitizer API have run with, and it doesn't seem like a terrible model to continue.

it's more like a complicated version of encoded into JSON and stuffed into the document as text

If this is what you're doing, why not continue using <link rel="prefetch">, pointing to an element as the data source? As a poorly thought-out strawman:

<datalist id="my-speculative-speculations">
{ ... }
</datalist>
<link rel="speculation-rules" href="#my-speculative-speculations">

and

<link rel="speculation-rules" href="https://mikewest.org/some-amazing-speculations">

That model seems clearer to me than imbuing <script>-based data blocks with behavior. The explainer doc has a good description of the reasons to avoid adding behavior to <link rel="prefetch">, but notes "The only real workaround for this is to invent a new rel="" value which has different behavior, e.g., pays attention to a new requirements="" attribute." There's a little more discussion of that approach in the "Alternatives to Speculation-Rules header" section, but none of the objections seem (to me, with very little thought put into it!) as dispositive. We could change objectionable defaults, we could ignore multiple attributes, etc.

@arturjanc
Copy link

I have to admit that I am worried about this change and think it will result in a security regression for existing sites using CSP.

The reason for this is that a lot of websites have policies that use script-src but the use of prefetch-src is quite uncommon. As a result, an HTML injection on most sites with CSP would be able to inject speculation rules; while I agree with @domenic that speculation rules are not in the same "league of power" as scripts, they still have powerful capabilities. Just looking at the explainer, it appears that they permit e.g. overriding the page's Referrer Policy and may in the future allow querying against href values -- this would let an attacker leak capability URLs and other secrets that an attacker wouldn't otherwise be able to read (assuming the page defines a reasonable script-src and style-src to prevent attribute exfiltration via CSS selectors).

I also agree with @mikewest's argument about it being simpler conceptually if script-src applies to all <script> elements. But even if we used a different element, the security concern still remains unless we e.g. introduce a new CSP directive that would fall back to script-src if the specific directive is not present in the policy.

Given all of this, my guess is that it will be simpler to keep this gated on script-src -- developers definitely don't have to add 'unsafe-inline' to their policies, they can use script hashes or nonces instead, without reducing the security of their CSPs.

@toyoshim
Copy link
Contributor Author

I don't know much history and reasons to decide using <script> for the Speculation-Rules, but @mikewest 's link+datalist approach also looks nice as an alternative Speculation-Rules entrypoint. I want to hear more opinions from people who are familiar to the history better than I am. Why did we take the script approach, and are we positive to introduce alternative syntax?

If I stick to my proposal, the risk @arturjanc raised can be solved if we also introduce speculation-rules-src as its fallback directive is set to script-src. If developers specifies the speculation-rules-src 'unsafe-inline' explicitly, we permit the inline speculation rules. Otherwise, we respect the script-src. Thus, we can avoid a security regression without existing developers' extra attention.

@hiroshige-g
Copy link

Having some <script> controlled by script-src and other <script> not controlled seems confusing (script-src alone doesn't kill all <script>, it would get harder to track which <script> is controlled by script-src especially when there are more and more new script types, which might lead security issues). Anyway existing JS codes use <script> with unsupported types as inline data placeholder (not related to scripting at all), but I feel we should think twice before bringing this kind of complexity to browsers.

So my initial thought was leaning toward either:

  • Using and extending non-<script> tags for non-powerful json-requiring features. (not so sure concretely, and adding complexity to parsers also has its cost and risk)
  • And/or, extending <script> in a principled way. Similar to Declarative <script> behavior whatwg/html#7986, we anyway have APIs using <script> with motivations for non-classic-script-like behavior (script-src CSP exemption, dynamically reflecting text contents, etc.), principles governing general <script>s and types might be needed. Still in this direction, the simplest and easiest-to-understand way to exempt from script-src might be to use non-<script> tags though.

@toyoshim
Copy link
Contributor Author

Thank you for duscssion. I agreed that my proposal would introduce a confusion around the relationship between <script> and script-src.

During a local chat, @domenic thinks of an interesting idea to introduce a new source keyword. For instance, script-src 'inline-speculation-rules' may be a good common ground?

@toyoshim
Copy link
Contributor Author

Here is a draft for the new proposal. I will make another PR if there is no objection here.

Content-Security-Policy

Speculation-Rules is embedded as an inline script in a script tag with type=”speculationrules”, and restricted by the script-src restriction of the Content-Security-Policy. In addition to ‘unsafe-inline’, ‘inline-speculation-rules’ are available to embed the inline Speculation-Rules. This helps developers to permit only Speculation-Rules but still to disallow unsafe inline JavaScript.

@toyoshim
Copy link
Contributor Author

To @jeremyroman @mikewest @arturjanc @hiroshige-g
Do you have any concern on the last proposal, script-src 'inline-speculation-rules' ?

@jeremyroman
Copy link
Collaborator

The <datalist> element doesn't have parser behavior suitable for non-HTML contents; only <style> and <script> do as far as I'm aware. (They're not completely immune to the quirks being read by the HTML parser and thus not needing escaping, but they mostly are.) Moreover, both speculation rules and import maps are currently shipping in Chromium using the <script> element for this purpose. Import maps is also shipping in other browsers, using <script>.

Yes, a fresh syntax could have been developed which was suitable for use in HTML attributes (and defined a processing model contrary to the existing <link rel=prefetch>) and which also had a different variant suitable for use in an external subresource. I cannot recall at the time anyone strongly favouring such an option (since it wouldn't be a straightforward backward-compatible extension), which is why it was not deeply explored.

Personally I think of script-src as relating to script (i.e., JavaScript) execution, not to the <script> element (for instance, it also confines use of eval()). Similarly I believe style-src confines <link>, <?xml-stylesheet?> and CSSImportRule as well as <style>. So a separate speculation-rules-src directive doesn't seem that inconsistent to me.

A separate 'inline-speculation-rules' keyword seems like a totally acceptable option to me, too. If others prefer that, I have no objection.

@toyoshim
Copy link
Contributor Author

Thank you @jeremyroman for the inputs on the reasoning of using the script tag.
I had a local chat with @mikewest and @arturjanc and now we reached consensus that the inline-speculation-rules is an acceptable option. So, I will make and send another PR.

Thank you everyone for the great discussion!

@toyoshim
Copy link
Contributor Author

Let me close this PR in favor or #213

@toyoshim toyoshim closed this Nov 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants